#pragma once

#include <iostream>
#include <string>
#include <cstring>
#include <sys/select.h>
#include "Sock.hpp"
#include "Log.hpp"

using namespace std;
const static int gport = 8080;
//typedef int type_t;
#define READ_EVENT (0x1)
#define WRITE_EVENT (0x1 << 1)
#define WXCEPT_EVENT (0x1 << 2)

typedef struct FdEvent{
    int fd;
    uint8_t event;
    string clientIp;
    uint16_t clientPort;
}type_t;

static const int defaultEvent = 0;

class SelectServer{
    static const int N = (sizeof(fd_set) * 8);

public:
    SelectServer(uint16_t port = gport):port_(port){

    }

    void InitServer(){
        listenSock_.Socket();
        listenSock_.Bind(port_);
        listenSock_.Listen();

        for(int i = 0; i < N; i++){
            fdArray_[i].fd = defaultSock;
            fdArray_[i].event = defaultEvent;
            fdArray_[i].clientPort = 0;
        }
    }

    void Accepter(){
        string clientIp;
        uint16_t clientPort;
        int sock = listenSock_.Accept(&clientIp, &clientPort);
        if(sock < 0)
            return;
        //得到了sock, 先不进行read/recv, 因为sock上不一定有数据就绪
        //将sock交给select, 让select进行管理
        logMessage(Debug, "[%s: %d], sock: %d", clientIp.c_str(), clientPort, sock);
        int pos = 1;
        for(; pos < N; pos++){
            if(fdArray_[pos].fd == defaultSock)
                break;
        }
        if(pos >= N){
            close(sock);
            logMessage(Warning, "sockfd array full");
        }
        else{
            fdArray_[pos].fd = sock;
            fdArray_[pos].event = READ_EVENT;
            fdArray_[pos].clientIp = clientIp;
            fdArray_[pos].clientPort = clientPort;
        }
    }

    void Recer(int index){
        int fd = fdArray_[index].fd;
        char buffer[1024];
        ssize_t s = recv(fd, buffer, sizeof(buffer)-1, 0);
        if(s > 0){
            buffer[s-1] = 0;
            cout << "client# " << buffer << endl;
            string echo = buffer;
            echo += "[selectServer echo]";
            send(fd, echo.c_str(), echo.size(), 0);
        }
        else{
            if(s == 0)
                logMessage(Info, "client quit ..., fdarray_[i] -> defaultfd: %d->%d", fd, defaultSock);
            else
                logMessage(Warning, "recv error, client quit ..., fdarray_[i] -> defaultfd: %d->%d", fd, defaultSock);
            close(fdArray_[index].fd);
            fdArray_[index].fd = defaultSock;
            fdArray_[index].event = defaultEvent;
            fdArray_[index].clientIp.resize(0);
            fdArray_[index].clientPort = 0;
        }
    }

    void HandlerEvent(fd_set &rfds){
        for(int i = 0; i < N; i++){
            if(fdArray_[i].fd == defaultSock)
                continue;
            if((fdArray_[i].event & READ_EVENT) && (FD_ISSET(fdArray_[i].fd, &rfds))){
                if(fdArray_[i].fd == listenSock_.GetSock())
                    Accepter();
                else if(fdArray_[i].fd != listenSock_.GetSock())
                    Recer(i);
                else{

                }
            }
            else if((fdArray_[i].event & WRITE_EVENT) && (FD_ISSET(fdArray_[i].fd, &rfds))){
                //ServerceIO()
            }
            else{
                
            }
        }
    }

    void Start(){
        fdArray_[0].fd = listenSock_.GetSock();
        fdArray_[0].event = READ_EVENT;

        while(true){
            fd_set rfds;
            fd_set wfds;
            FD_ZERO(&rfds);
            FD_ZERO(&wfds);
            int maxFd = fdArray_[0].fd;
            for(int i = 0; i < N; i++){
                if(fdArray_[i].fd == defaultSock)
                    continue;//不是-1的才写入rfds进行管理
                if(fdArray_[i].event & READ_EVENT)
                    FD_SET(fdArray_[i].fd, &rfds);                
                if(fdArray_[i].event & WRITE_EVENT)
                    FD_SET(fdArray_[i].fd, &wfds);
                if(maxFd < fdArray_[i].fd)
                    maxFd = fdArray_[i].fd;
            }

            int n = select(maxFd+1, &rfds, &wfds, nullptr, nullptr);
            switch(n){
                case 0:
                    logMessage(Debug, "timeout, %d: %s", errno, strerror(errno));
                    break;
                case -1:
                    logMessage(Warning, "%d: %s", errno, strerror(errno));
                    break;
                default:
                    logMessage(Debug, "happen: %d", n);
                    HandlerEvent(rfds);
                    DebugPrint();
                    break;
            }
            //sleep(1);
        }
    }

    void DebugPrint(){
        cout << "fdArray[]: ";
        for(int i = 0; i < N; i++){
            if(fdArray_[i].fd == defaultSock)
                continue;
            cout << fdArray_[i].fd << " ";
        }
        cout << endl;
    }

    ~SelectServer(){
        listenSock_.Close();
    }

private:
    uint16_t port_;
    Sock listenSock_;
    type_t fdArray_[N];
};